/* 
 *	Copyright (C) 2009-2010 miaohua
 *	http://code.google.com/p/lrcshower/
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include "stdafx.h"
#include <string>
#include <fstream>
#include <memory>
#include <iterator>
#include "TypeDef.h"
#include "resource.h"
#include "ParseErrCode.h"
#include "SearchEngineComHdr.h"
#include "SogouMusic.h"
#include "HelperFunctions.h"
#include "SogouPageParser.h"
#include "SogouLrcParser.h"
#include "SimpleFileDownloader.h"
#include "SimpleWebPageParser.h"
#include "SogouGenUrlHelperFunctions.h"
using namespace std;


extern HMODULE gbl_ModuleHandler;


//bool Result2XML(IPageParser* parser, const tstring & songName, const tstring & fullPath)
//{
//         CSogouSongParser * SongParser = dynamic_cast<CSogouSongParser*>(parser);
//         if( SongParser == NULL )
//              return false;
//         
//         int size = SongParser->NumOfSong();
//         if( size < 0 )
//              return false;
//
//         //create xml document
//         auto_ptr<TiXmlDocument> list = auto_ptr<TiXmlDocument>(new TiXmlDocument());
//         //create declaration
//         TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "GB2312", "" );
//         list->LinkEndChild(decl);
//         //create the root element
//         TiXmlElement* rootElement = new TiXmlElement("MusicBox2");
//         rootElement->SetAttribute("number", size);
//         rootElement->SetAttribute("name", WS2MB(songName).c_str());
//         list->LinkEndChild(rootElement);
//         
//          //insert songs 
//          for( int i = 0; i < size; ++i )
//          {     
//                  //generate the node name
//                  string nodeName = "Song" + lexical_cast<string>(i);
//                  TiXmlElement* song = new TiXmlElement(nodeName.c_str());
//                  //index: which song, attribute num: the number of attributes that song has
//                  song->SetAttribute("index", i);
//                  song->SetAttribute("attributenum", AttributeNum);
//
//                  //set attributes
//                  //name
//                  song->SetAttribute(AttributeName[0], WS2MB(tuples::get<1>((*SongParser)[i])).c_str());
//                  //singer;
//                  song->SetAttribute(AttributeName[1], WS2MB(tuples::get<3>((*SongParser)[i])).c_str());
//                  //album;
//                  song->SetAttribute(AttributeName[2], WS2MB(tuples::get<4>((*SongParser)[i])).c_str());
//                  //size;
//                  song->SetAttribute(AttributeName[3], WS2MB(tuples::get<5>((*SongParser)[i])).c_str());
//                  //format;
//                  song->SetAttribute(AttributeName[4], WS2MB(tuples::get<6>((*SongParser)[i])).c_str());
// 
//                  //and to song list
//                  rootElement->LinkEndChild(song);
//          }
//
//          //save xml file
//         return list->SaveFile(WS2MB(fullPath).c_str());
//}
//
class CBitmapHelper
{
public:
     CBitmapHelper(unsigned int ResId);
     ~CBitmapHelper();
     HBITMAP GetHandler() const;
private:
     HBITMAP m_BitPic;
};


CBitmapHelper::CBitmapHelper(unsigned int resId)
{
     m_BitPic = LoadBitmap(gbl_ModuleHandler, MAKEINTRESOURCE(resId));
} 

CBitmapHelper::~CBitmapHelper()
{
     if( m_BitPic )
         DeleteObject(m_BitPic);
}

HBITMAP CBitmapHelper::GetHandler() const
{
     return m_BitPic;
}

class CSogouMusicImp
{
public:
	CSogouMusicImp();
	int SearchSong(const tstring & Song, tstring & ErrMsg, SongList & List, int Page=0);
    int SearchLrc(const tstring & Song, const tstring & Singer, tstring & ErrMsg, LrcList & List, int Page=0);
    bool GetMp3DownloadUrl(int Idx, tstring & Url);
    bool GetLrcDownloadUrl(int Idx, tstring & Url);
    int GetLrcTotalNum()const;
    int GetSongTotalNum()const;
    bool GetEngineLogo(HBITMAP & Logo) const;
    const TCHAR* GetEngineName() const;
    
private:
    static const int m_SongNumPerPage = 30;                              
    static const TCHAR * m_EngineName;

    tstring m_SongName;                                                                         //the song name
    CBitmapHelper m_EngineLogo;
    CSogouSongParser  m_SongParser;
    CSogouLrcParser   m_LrcParser;
    CSogouLrcNumParser  m_LrcNumParser;
    CSogouSongNumParser m_SongNumParser;
    std::auto_ptr<IFileDownloader> m_Downloader;
    std::auto_ptr<IWebPageParse>   m_MusicWebPageParse;
};

const int CSogouMusicImp::m_SongNumPerPage;
const TCHAR * CSogouMusicImp::m_EngineName = _T("Sogou");

CSogouMusicImp::CSogouMusicImp():m_EngineLogo(IDB_SOGOU_LOGO)
{
     // Create the download
     m_Downloader = auto_ptr<IFileDownloader>(new CSimpleFileDownloader());

     // Create web page parser
     m_MusicWebPageParse = auto_ptr<IWebPageParse>(new CSimpleWebPageParser());
}

int CSogouMusicImp::SearchSong(const tstring & SongName, tstring & ErrMsg, SongList & List, int Page)
{ 
     // Check wether song name is empty
     if( SongName.empty() )
     {
         ErrMsg = AppErrMsg[E_SongName_Empty];
         return -1;
     }

     m_SongName = SongName;
     // Generate the get html baidu mp3 url
     tstring Url = SogouMP3UrlGen(m_SongName, Page+1);
     // Get the application's tmp directory
     tstring DataBuf;
#ifdef _DEBUG
     tstring TmpPath;
     if( !GetTmpDir(TmpPath, HTML_TMP_DIR) )
     {
         ErrMsg = AppErrMsg[E_GetTmpDir_Fail];
         return -1;
     }
     DataBuf = TmpPath + tstring(_T("\\")) + m_SongName + SONGLIST_EXT;
#endif
     //download the search result web page
     if( !m_Downloader->StartFileDownload(Url, DataBuf)  )
     {
         ErrMsg = AppErrMsg[E_DownloadList_Fail];
         return -1;
     }

     // Regex parse web page
     // Get the content of html page
     tstring HtmlSrc;
#ifdef _DEBUG
     ifstream in(DataBuf.c_str());
     string Data((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
         
#ifdef UNICODE
     MultiBytes2WildStr(Data, HtmlSrc);
#else
     HtmlSrc = Data;
#endif
#else
     HtmlSrc = DataBuf;
#endif

     // Regiser parsers
     m_MusicWebPageParse->RegisterParser(&m_SongParser);
     m_MusicWebPageParse->RegisterParser(&m_SongNumParser);

     // Do parse
     m_MusicWebPageParse->Parse(HtmlSrc);  

     // Clear
     m_MusicWebPageParse->ClearParser();

     // Get song list in page
     m_SongParser.GetSongList(List);

     return List.size();
}

int CSogouMusicImp::SearchLrc(const tstring & Song, const tstring & Singer, tstring & ErrMsg, LrcList & List, int Page)
{
     // Check wether song name is empty
     if( Song.empty() )
     {
         ErrMsg = AppErrMsg[E_SongName_Empty];
         return -1;
     }

     // Generate the get html sogou lrc url
     tstring Url = SogouLrcUrlGen(Song, Singer, Page);

     // Get the application's tmp directory & lrc file's full path
     // If in debug mode, we download the lrc file on harddisk;
     // If in release mode, we just download in memory
     tstring DataBuf;
#ifdef _DEBUG
     tstring TmpPath;
     if( !GetTmpDir(TmpPath, HTML_TMP_DIR) )
     {
         ErrMsg = AppErrMsg[E_GetTmpDir_Fail];
         return -1;
     }

     DataBuf = TmpPath + tstring(_T("\\")) + Song + SONGLRC_EXT;
#endif
     // Download the song download url web page
     if( !m_Downloader->StartFileDownload(Url, DataBuf)  )
     {
         ErrMsg = AppErrMsg[E_DownloadList_Fail];
         return -1;
     }

     // Regex parse web page
     // Get the content of html page
     tstring HtmlSrc;
#ifdef _DEBUG
     ifstream in(DataBuf.c_str());
     string data((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
#ifdef UNICODE
     MultiBytes2WildStr(data, HtmlSrc);
#else
     HtmlSrc = data;
#endif
#else
     HtmlSrc = DataBuf;
#endif

     // Regiser parsers
     m_MusicWebPageParse->RegisterParser(&m_LrcParser);
     m_MusicWebPageParse->RegisterParser(&m_LrcNumParser);

     // Do parse
     m_MusicWebPageParse->Parse(HtmlSrc);  

     // Get result
     m_LrcParser.GetLrcList(List);

     // Clear parser
     m_MusicWebPageParse->ClearParser();

     return m_LrcParser.ResultCount();    
}

bool CSogouMusicImp::GetMp3DownloadUrl(int Idx, tstring & SongUrl)
{
     if( Idx >= m_SongParser.ResultCount() ) return false;

     // Get the song's download promt page url
     tstring Url = Sogou_Music_Main + m_SongParser[Idx].url;
     // Download the promt page
     // Here in debug mode, we download the file on local disk
     // But in release mode, we just download it in memory to speed up
     tstring DataBuf;
#ifdef _DEBUG
     tstring FileName = m_SongName + IntToStr(Idx); 
     //get the application's tmp directory
     tstring TmpPath;
     if( !GetTmpDir(TmpPath, HTML_TMP_DIR) )
     {
          _Dbg_Printf(AppErrMsg[E_GetTmpDir_Fail]);
          return false;
     }
     DataBuf = TmpPath + tstring(_T("\\")) + FileName + DOWNLOAD_URL_EXT; 
#endif
     // Download the song download url web page
     // The loader is able to judge wether to download on disk or memory according to the parameter *DataBuf*
     // If DataBuf is empty, then load to memory, else download to disk
     if( !m_Downloader->StartFileDownload(Url, DataBuf)  )
     {
#ifdef _DEBUG
         _Dbg_Printf(AppErrMsg[E_DownloadList_Fail]);
#endif
         return false;
     }

     tstring HtmlSrc;
#ifdef _DEBUG
     ifstream in(DataBuf.c_str());
     string Data((istreambuf_iterator<char>(in)), istreambuf_iterator<char>());
#ifdef UNICODE
     MultiBytes2WildStr(Data, HtmlSrc);
#else
     HtmlSrc = Data;
#endif
#else
     HtmlSrc = DataBuf;
#endif

     // UrlParser
     CSogouSongUrlParser UrlParser;

     // Register parser
     m_MusicWebPageParse->RegisterParser(&UrlParser);
        
     // Do parse
     m_MusicWebPageParse->Parse(HtmlSrc);

     // Clear
     m_MusicWebPageParse->ClearParser();

     // If we are unable to get the file's down load url, then prompt to user
     if( UrlParser.DownloadUrl().empty() )
     {
#ifdef _DEBUG
         _Dbg_Printf(AppErrMsg[E_DownloadList_Fail]);
#endif
         return false;
     }      

     // Save it
     SongUrl = UrlParser.DownloadUrl();
     return true;
}

bool CSogouMusicImp::GetLrcDownloadUrl(int Idx, tstring & Url)
{
     if( Idx >= m_LrcParser.ResultCount() ) return false;

     Url = Sogou_Music_Main + m_LrcParser[Idx].url;
     return true;
}

bool CSogouMusicImp::GetEngineLogo(HBITMAP & Logo)const
{
     Logo = m_EngineLogo.GetHandler();
     //check wether loading bitmap is successful
     if( Logo == NULL ) return false;
     return true;
}

const TCHAR* CSogouMusicImp::GetEngineName() const
{
     return m_EngineName;
}

int CSogouMusicImp::GetLrcTotalNum()const
{
     return m_LrcNumParser.TotalNum();
}

int CSogouMusicImp::GetSongTotalNum()const
{
     return m_SongNumParser.TotalNum();
}


CSogouMusicImp Gbl_SogouSearchEngine;

const TCHAR* __stdcall SogouGetEngineName()
{
     return Gbl_SogouSearchEngine.GetEngineName();
}

bool __stdcall SogouGetEngineLogo(HBITMAP & Logo)
{
     return Gbl_SogouSearchEngine.GetEngineLogo(Logo);
}

int __stdcall SogouSearchSong(const tstring & Song, tstring & ErrMsg, SongList & List, int Page)
{
     return Gbl_SogouSearchEngine.SearchSong(Song, ErrMsg, List, Page);
}

int __stdcall SogouSearchLrc(const tstring & Song, const tstring & Singer, tstring & ErrMsg, LrcList & List, int Page)
{
     return Gbl_SogouSearchEngine.SearchLrc(Song, Singer, ErrMsg, List, Page);
}

bool __stdcall SogouGetLrcDownloadUrl(int Idx, tstring & Url)
{
     return Gbl_SogouSearchEngine.GetLrcDownloadUrl(Idx, Url);    
}

bool __stdcall SogouGetMp3DownloadUrl(int Idx, tstring & Url)
{
     return Gbl_SogouSearchEngine.GetMp3DownloadUrl(Idx, Url);
}

int __stdcall SogouGetLrcTotalNum()
{
     return Gbl_SogouSearchEngine.GetLrcTotalNum();    
}

int __stdcall SogouGetSongTotalNum()
{
     return Gbl_SogouSearchEngine.GetSongTotalNum();    
}

